﻿using System;
using System.Collections.Generic;
using System.Reflection;

namespace Ooaavee.Scripting
{
    /// <summary>
    /// Loads referenced assemblies when the the resulution of assemblies fails.
    /// </summary>
    [Serializable]
    internal sealed class AssemblyResolver
    {
        private const string Key = "key";

        private ScriptingContext _context;
        
        private readonly Dictionary<string, Assembly> _assemblies = new Dictionary<string, Assembly>();

        /// <summary>
        /// No public constructor available.
        /// </summary>
        private AssemblyResolver()
        {            
        }

        internal static void Initialize(AppDomain domain, ScriptingContext context)
        {
            domain.SetData(Key, new AssemblyResolver { _context = context });
            domain.AssemblyResolve += Resolve;            
        }

        internal static Assembly Resolve(object sender, ResolveEventArgs args)
        {
            AssemblyResolver resolver = (AssemblyResolver)((AppDomain)sender).GetData(Key);

            Assembly assembly;

            // First try to get a previously loaded assembly.
            bool found = resolver._assemblies.TryGetValue(args.Name, out assembly);
            if (found)
            {
                return assembly;
            }

            // If the resolved assembly name is referenced, then load it.
            foreach (Reference reference in resolver._context.References)
            {
                if (reference.AssemblyFullName.Equals(args.Name, StringComparison.CurrentCulture))
                {
                    assembly = Assembly.LoadFrom(reference.AssemblyFile.ToString());
                    resolver._assemblies[args.Name] = assembly;
                    return assembly;
                }
            }

            return null;
        }

    }
}
